<?php

// 应用公共文件

// 数族分组
use app\exception\TokenException;
use EasySwoole\Jwt\Jwt;

function getArrayGroupBy($data, $key): array
{
    $result = [];

    foreach ($data as $value) {
        if (array_key_exists($key, $value)) {
            $groupValue = $value[$key];
            if (!array_key_exists($groupValue, $result)) {
                $result[$groupValue] = array();
            }
            $result[$groupValue][] = $value;
        }
    }

    return $result;
}

function strToUtf8($str)
{

    $encode = mb_detect_encoding($str, array('CP936', "ASCII", "GB2312", "GBK", 'UTF-8', 'BIG5'));

    if ($encode == 'UTF-8') {

        return $str;

    } else {

        return mb_convert_encoding($str, 'UTF-8', $encode);

    }

}

/**
 * 对富文本信息中的数据
 * 匹配出所有的 <img> 标签的 src属性
 * @param string $contentStr 富文本字符串
 * @return array
 *
 */
function getPatternMatchImages(string $contentStr = ''): array
{
    $imgSrcArr = [];
    //首先将富文本字符串中的 img 标签进行匹配
    $pattern_imgTag = '/<img\b.*?(?:\>|\/>)/i';
    preg_match_all($pattern_imgTag, $contentStr, $matchIMG);
    if (isset($matchIMG[0])) {
        foreach ($matchIMG[0] as $key => $imgTag) {
            //进一步提取 img标签中的 src属性信息
            $pattern_src = '/\bsrc\b\s*=\s*[\'\"]?([^\'\"]*)[\'\"]?/i';
            preg_match_all($pattern_src, $imgTag, $matchSrc);
            if (isset($matchSrc[1])) {
                foreach ($matchSrc[1] as $src) {
                    //将匹配到的src信息压入数组
                    $imgSrcArr[] = $src;
                }
            }
        }
    }
    //$pattern= '/<img\b.+\bsrc\b\s*=\s*[\'\"]([^\'\"]*)[\'\"]/iU';
    return $imgSrcArr;
}


// 获取文件夹下文件数量
function getFileNumber($path): int
{
    $handle = opendir($path);
    $i = 0;
    while (false !== $file = (readdir($handle))) {
        if ($file !== '.' && $file != '..') {
            $i++;
        }
    }
    closedir($handle);
    return $i;
}

// 手机版访问
function isMobile(): bool
{
    return request()->isMobile();
}

// 按照字符串个数切割字符串
function hcSubstr($str, $len, $start = 0): string
{
    if (empty($str)) {
        return '';
    }
    //返回字符串中的前100字符串长度的字符
    return mb_substr($str, $start, $len, 'utf-8');
}

// sql切割
function hcSplitSql($file, $prefix, $charset = 'utf8mb4', $defaultTablePre = '$prefix$', $defaultCharset = 'utf8mb4')
{
    if (file_exists($file)) {
        //读取SQL文件
        $sql = file_get_contents($file);
        $sql = str_replace("\r", "\n", $sql);
        $sql = str_replace("BEGIN;\n", '', $sql);//兼容 navicat 导出的 insert 语句
        $sql = str_replace("COMMIT;\n", '', $sql);//兼容 navicat 导出的 insert 语句
        $sql = str_replace($defaultCharset, $charset, $sql);
        $sql = trim($sql);
        //替换表前缀
        $sql = str_replace("{$defaultTablePre}", "{$prefix}", $sql);
        $sqls = explode(";\n", $sql);
        return $sqls;
    }

    return [];
}

function sp_testwrite($d)
{
    $tfile = "_test.txt";
    $fp = @fopen($d . "/" . $tfile, "w");
    if (!$fp) {
        return false;
    }
    fclose($fp);
    $rs = @unlink($d . "/" . $tfile);
    if ($rs) {
        return true;
    }
    return false;
}

function sp_dir_create($path, $mode = 0777)
{
    if (is_dir($path))
        return true;
    $ftp_enable = 0;
    $path = sp_dir_path($path);
    $temp = explode('/', $path);
    $cur_dir = '';
    $max = count($temp) - 1;
    for ($i = 0; $i < $max; $i++) {
        $cur_dir .= $temp[$i] . '/';
        if (@is_dir($cur_dir))
            continue;
        @mkdir($cur_dir, 0777, true);
        @chmod($cur_dir, 0777);
    }
    return is_dir($path);
}

function sp_dir_path($path)
{
    $path = str_replace('\\', '/', $path);
    if (substr($path, -1) != '/')
        $path = $path . '/';
    return $path;
}

function sp_execute_sql($db, $sql)
{
    $sql = trim($sql);
    if (substr($sql, 0, 12) == 'CREATE TABLE') {
        $table_name = preg_replace("/^CREATE TABLE `(\w+)` .*/s", "\\1", $sql);;
        $msg = "创建数据表 {$table_name} ";
        try {
            $db->execute($sql);
            return [
                'type' => 1, // 创建表
                'error' => 0,
                'message' => $msg . '成功！'
            ];
        } catch (\Exception $e) {
            return [
                'type' => 1, // 创建表
                'error' => 1,
                'message' => $msg . '失败！',
                'exception' => $e->getMessage()
            ];
        }

    } else {
        try {
            $db->execute($sql);
            return [
                'type' => 2, // 执行sql语句
                'error' => 0,
                'message' => 'SQL执行成功!'
            ];
        } catch (\Exception $e) {
            return [
                'type' => 2, // 执行sql语句
                'error' => 1,
                'message' => 'SQL执行失败！',
                'exception' => $e->getMessage()
            ];
        }
    }
}

function hcInstalled(): bool
{
    return file_exists(CMS_ROOT . '/public/system_file/install.lock');
}

function createInstallFile()
{
    @file_put_contents(CMS_ROOT . '/public/system_file/install.lock', file_get_contents(CMS_ROOT .'/version'));
}

function updateVersion()
{
    if (file_get_contents(CMS_ROOT .'/version') != @file_get_contents(CMS_ROOT .'/public/system_file/install.lock')) {
        createInstallFile();
    }
}


/**
 * @throws \app\exception\ModelException
 * @throws \app\exception\ModelEmptyException
 */
function hcUrl($url, $vars, $suffix = true, $domain = false): \think\route\Url
{
    $url = strtolower($url);
    if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
        $info = parse_url($url);
        $url = !empty($info['path']) ? $info['path'] : '';
        if (isset($info['fragment'])) {
            // 解析锚点
            $anchor = $info['fragment'];
            if (false !== strpos($anchor, '?')) {
                // 解析参数
                list($anchor, $info['query']) = explode('?', $anchor, 2);
            }
            if (false !== strpos($anchor, '@')) {
                // 解析域名
                list($anchor, $domain) = explode('@', $anchor, 2);
            }
        } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
            // 解析域名
            list($url, $domain) = explode('@', $url, 2);
        }
    }
    if (0 == strpos($url, '/')) {
        $url = ltrim($url, '/');
    }
    // 解析参数
    if (is_string($vars)) {
        // aaa=1&bbb=2 转换成数组
        parse_str($vars, $vars);
    }

    if (isset($info['query'])) {
        // 解析地址里面参数 合并到vars
        parse_str($info['query'], $params);
        $vars = array_merge($params, $vars);
    }

    if (!empty($anchor)) {
        $url = $url . '#' . $anchor;
    }
    if ($url == 'list/index' && isset($vars['id'])) {
        $url = $url . '?id=' . $vars['id'];
        unset($vars['id']);
    }
    if ($url == 'detail/index' && isset($vars['cid'])) {
        $url = $url . '?cid=' . $vars['cid'];
        unset($vars['cid']);
    }
    if ($url == 'tag/index') {
        $url = $url . '?id=' . $vars['id'];
    }
    return url($url, $vars, $suffix, $domain);
}


function optEventLog($id, $title, $operation)
{
    if (is_array($id)) {
        $id = implode(',', $id);
    }
    $optAdmin = request()->admin;
    event("AdminOptLog", [
        'admin_id' => $optAdmin['uid'],
        'admin_name' => $optAdmin['name'],
        'title' => $title . lang("管理"),
        "content" => $operation . $title . ',' . $title . lang('ID为') . $id,
    ]);
}

/**
 * 判断是否为windows系统
 * @return bool
 */
function windows_os(): bool
{
    return PHP_OS_FAMILY === 'Windows';
}

function is_directory($directory): bool
{
    return is_dir($directory);
}

// 获取文件后缀
function getFileExt($str): string
{
    return strtolower(pathinfo($str, PATHINFO_EXTENSION));
}

/**
 * 返回文件格式
 * @param string $str 文件名
 * @return string      文件格式
 */
function fileFormat(string $str): string
{
    // 取文件后缀名
    $str = getFileExt($str);
    // 图片格式
    $image = array('webp', 'jpg', 'png', 'ico', 'bmp', 'gif', 'tif', 'pcx', 'tga', 'bmp', 'pxc', 'tiff', 'jpeg', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf', 'ufo', 'eps', 'ai', 'hdri');
    // 视频格式
    $video = array('mp4', 'avi', '3gp', 'rmvb', 'wmv', 'mkv', 'mpg', 'vob', 'mov', 'flv', 'swf', 'ape', 'wma', 'aac', 'mmf', 'amr', 'm4a', 'm4r', 'ogg', 'wav', 'wavpack', 'm4v', 'webm');
    // 音频格式
    $audio = array('mp3', 'mpeg', 'wma', 'mid', 'cd', 'wave', 'aiff', 'mpeg-4', 'midi', 'flac', 'acc', 'ape', 'amr');
    // 压缩格式
    $zip = array('rar', 'zip', 'tar', 'cab', 'uue', 'jar', 'iso', 'z', '7-zip', 'ace', 'lzh', 'arj', 'gzip', 'bz2', 'tz');
    // 文档格式
    $text = array('exe', 'doc', 'ppt', 'xls', 'wps', 'txt', 'lrc', 'wfs', 'torrent', 'html', 'htm', 'java', 'js', 'css', 'less', 'php', 'pdf', 'pps', 'host', 'box', 'docx', 'word', 'perfect', 'dot', 'dsf', 'efe', 'ini', 'json', 'lnk', 'log', 'msi', 'ost', 'pcs', 'tmp', 'xlsb','ttf', 'eot','woff','woff2');

    // 匹配不同的结果
    switch ($str) {
        case in_array($str, $image):
            return 'image';
        case in_array($str, $video):
            return 'video';
        case in_array($str, $zip):
            return 'zip';
        case in_array($str, $audio):
            return 'audio';
        default:
            return 'file';
    }
}

/**
 * 获取网站跟目录
 * @return string
 */
function hcGetRoot(): string
{
    $root = request()->root();
    $root = str_replace("//", '/', $root);
    $root = str_replace('/index.php', '', $root);
    if (defined('APP_NAMESPACE') && APP_NAMESPACE == 'api') {
        $root = preg_replace('/\/api$/', '', $root);
    }
    return rtrim($root, '/');
}

/**
 * 扫描路径下所有文件包括子目录
 * @param $dir
 * @return array
 */
function scanSubDir($dir): array
{
//    $dir     = ltrim($dir, "/");
    $dirs = [];
    $subDirs = hcScanDir("$dir/*", GLOB_ONLYDIR);
    if (!empty($subDirs)) {
        foreach ($subDirs as $subDir) {
            $subDir = "$dir/$subDir";
            array_push($dirs, $subDir);
            $subDirSubDirs = scanSubDir($subDir);
            if (!empty($subDirSubDirs)) {
                $dirs = array_merge($dirs, $subDirSubDirs);
            }
        }
    }
    return $dirs;
}

/**
 * 判断文件是否存在
 * @param $filename
 * @return bool
 */
function hcFileExist($filename): bool
{
    if (is_file($filename)) {
        if (env('app_debug')) {
            if (basename(realpath($filename)) != basename($filename))
                return false;
        }
        return true;
    }
    return false;
}

/**
 * 扫描路径下的文件
 * @param $pattern
 * @param null $flags
 * @return array
 */
function hcScanDir($pattern, $flags = null): array
{
    $files = glob($pattern, $flags);
    if (empty($files)) {
        $files = [];
    } else {
        $files = array_map('basename', $files);
    }
    return $files;
}

/**
 * 小驼峰格式
 *
 * @param string $uncamelized_words
 * @param string $separator
 * @return string
 */
function camelize(string $uncamelized_words, string $separator = '_'): string
{
    $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
    return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
}

/**
 * 下划线格式
 *
 * @param string $camelCaps
 * @param string $separator
 * @return string
 */
function uncamelize(string $camelCaps, string $separator = '_'): string
{
    return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}

/**
 * 模型内统一数据返回
 * @param $code
 * @param string $msg
 * @param array $data
 * @return array
 */
function dataReturn($code, $msg = 'success', $data = [])
{

    return ['code' => $code, 'data' => $data, 'msg' => $msg];
}

/**
 * 统一返回json数据
 * @param $code
 * @param string $msg
 * @param $data
 * @return \think\response\Json
 */
function jsonReturn($code, string $msg = 'success', $data = []): \think\response\Json
{

    return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
}

/**
 * 生成用户密码
 * @param $password
 * @param string $salt
 * @return string
 */
function makePassword($password, string $salt = ''): string
{

    if (empty($salt)) {
        $salt = config('system.salt');
    }

    return sha1(md5($password . $salt));
}

/**
 * 校验密码
 * @param $inputPassword
 * @param $dbPassword
 * @param string $salt
 * @return bool
 */
function checkPassword($inputPassword, $dbPassword, string $salt = ''): bool
{

    if (makePassword($inputPassword, $salt) == $dbPassword) {
        return true;
    }

    return false;
}

/**
 * 解析token中简短的用户信息
 * @param $token
 * @return array
 */
function getUserSimpleInfo($token): array
{

    try {

        $token = (new \Lcobucci\JWT\Parser())->parse($token);
    } catch (\Exception $e) {

        return dataReturn(-1, $e->getMessage());
    }

    $data = new \Lcobucci\JWT\ValidationData();

    $data->setIssuer($token->getClaim('iss'));
    $data->setAudience($token->getClaim('aud'));
    $data->setId($token->getClaim('jti'));

    if (!$token->validate($data)) {
        return dataReturn(-2, 'token validate');
    }

    return dataReturn(0, '', [
        'uid' => $token->getClaim('uid'),
        'name' => $token->getClaim('name'),
        'seller_id' => $token->getClaim('seller_id'),
    ]);
}

/**
 * 从头部获取token
 * @return bool|string
 */
function getHeaderToken()
{

    $token = '';
    $authorization = request()->header('authorization');
    if (!empty($authorization)) {
        $token = substr($authorization, 7);
    }
    return $token;
}

/**
 * 统一分页返回
 * @param $list
 * @return array
 */
function pageReturn($list): array
{
    if (0 == $list['code']) {
        return ['code' => 0, 'msg' => 'ok', 'count' => $list['data']->total(), 'data' => $list['data']->all()];
    }

    return ['code' => 0, 'msg' => 'ok', 'count' => 0, 'data' => []];
}

// 引用实现无限分类
function generate(array $data, $flag = false): array
{
    $data = getColumnForKeyArray($data, 'id');
    $tree = [];
    foreach ($data as $k => $v) {
        if (isset($data[$v['parent_id']])) {
            $data[$v['parent_id']]['children'][] = &$data[$k];
        } else {
            $tree[] = &$data[$k];
        }
    }
    return $tree;
}

// 数组值作为键
function getColumnForKeyArray(array $data, string $key): array
{
    $item = [];
    foreach ($data as $val) {
        if (!array_key_exists($key, $val)) {
            break;
        }
        $val['children'] = [];
        $item[$val[$key]] = $val;
    }
    return $item;
}

// 自定义树形结构层数
function makeTreeWithMaxLevel($data, $pid = 0, $maxLevel = 0, $level = 1): array
{
    $tree = array();
    foreach ($data as $key => $value) {
        if ($value['parent_id'] == $pid) {
            $value['level'] = $level;
            unset($data[$key]);
            if ($maxLevel === 0 || $maxLevel > $level) {
                $value['children'] = makeTreeWithMaxLevel($data, $value['id'], $maxLevel, $level + 1);
            } else {
                $value['children'] = [];
            }
            $tree[] = $value;
        }
    }
    return $tree;
}

/**
 * // 递归实现无限分类
 *
 * @param $data
 * @param int $pid
 * @param int $level
 * @return array
 */
function makeTree($data, int $pid = 0, int $level = 0): array
{
    $tree = array();
    foreach ($data as $key => $value) {
        $value['children'] = [];
        if ($value['parent_id'] == $pid) {
            $value['level'] = $level;
            unset($data[$key]);
            $value['children'] = makeTree($data, $value['id'], $level + 1);
            $tree[] = $value;
        }
    }
    return $tree;
}

/**
 * 获取某月所有时间
 * @param string $time 某天时间戳
 * @param string $format 转换的时间格式
 * @return array
 */
function getMonth($time = '', $format = 'Y-m-d')
{
    $time = $time != '' ? $time : time();
    // 获取当前周几
    $week = date('d', $time);
    $date = [];
    for ($i = 1; $i <= date('t', $time); $i++) {
        $date[date($format, strtotime('+' . ($i - $week) . ' days', $time))] = 0;
    }
    return $date;
}


/**
 * curl post数据
 * @param $url
 * @param $postData
 * @return array
 */
function curlPost($url, $postData)
{

    // 初始化
    $curl = curl_init();
    // 设置抓取的url
    curl_setopt($curl, CURLOPT_URL, $url);
    // 设置头文件的信息作为数据流输出
    curl_setopt($curl, CURLOPT_HEADER, 0);
    // 设置获取的信息以文件流的形式返回，而不是直接输出。
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    // 超时设置
    curl_setopt($curl, CURLOPT_TIMEOUT, 10);
    // 超时设置，以毫秒为单位
    // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    // 设置post方式提交
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
    // 执行命令
    $data = curl_exec($curl);
    curl_close($curl);

    return dataReturn(0, '成功', $data);
}

/**
 * curl get 请求
 * @param $url
 * @return array
 */
function curlGet($url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FAILONERROR, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_AUTOREFERER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    // $SSL = substr($url, 0, 8) == "https://" ? true : false;
    // if ($SSL) {
    //     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
    //     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名
    // }
    $data = curl_exec($ch);
    curl_close($ch);
    return dataReturn(0, '成功', $data);
}

/**
 * 两个日期间全部的数据
 * @param $start
 * @param $end
 * @return array
 */
function getBetweenDate($start, $end)
{

    $returnDate = [];
    $dtStart = strtotime($start);
    $dtEnd = strtotime($end);
    while ($dtStart <= $dtEnd) {
        $returnDate[] = date('m-d', $dtStart);
        $dtStart = strtotime('+1 day', $dtStart);
    }

    return $returnDate;
}


/**
 * 获取树形结构指定值子级
 * @param $tree
 * @param $name
 * @param $value
 * @return array
 */
function getTreeDataValue($tree, $name, $value)
{
    $resTree = array();
    foreach ($tree as $item) {
        if ($item[$name] == $value) {
            $resTree[] = $item[$name];
            if (is_array($item) && isset($item['children'])) {
                $resTree = array_merge($resTree, getTreeData($item['children'], $name));
            }
        } else {
            if (is_array($item) && isset($item['children'])) {
                $resTree = array_merge($resTree, getTreeDataValue($item['children'], $name, $value));
            }
        }
    }
    return $resTree;
}


/**
 * 获取树形结构指定值
 * @param $tree
 * @param $name
 * @param $value
 * @return array
 */
function getTreeData($tree, $name)
{
    $resTree = array();
    foreach ($tree as $item) {
        $resTree[] = $item[$name];
        if (is_array($item) && isset($item['children'])) {
            $resTree = array_merge($resTree, getTreeData($item['children'], $name));
        }
    }
    return $resTree;
}


/**
 * 生成下载表格
 * @param $fileName
 * @param $sheetName
 * @param $data
 * @param $matchedData
 * @return array
 */
function createDownloadExcel($fileName, $sheetName, $head, $data, $matchedData)
{
    $config = [
        'path' => config('suwork.excel_url'),
    ];

    $fileName = $fileName . date('Y_m_d_H_i') . '.xlsx';
    $xlsxObject = new \Vtiful\Kernel\Excel($config);

    $formatData = [];
    foreach ($data as $key => $value) {
        foreach ($matchedData as $k => $val) {
            if (isset($value[$k])) {
                if (is_array($val)) {
                    if (isset($val[$value[$k]])) {
                        $formatData[$key][] = $val[$value[$k]];
                    } else {
                        $formatData[$key][] = '';
                    }
                } else {
                    $formatData[$key][] = $value[$k];
                }
            } else {
                $formatData[$key][] = '';
            }
        }
    }
    $formatData = array_values($formatData);

    $filePath = $xlsxObject->fileName($fileName, $sheetName)
        ->header($head)
        ->data($formatData)
        ->output();
    // Set Header
    header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    header('Content-Disposition: attachment;filename="' . $fileName . '"');
    header('Content-Length: ' . filesize($filePath));
    header('Content-Transfer-Encoding: binary');
    header('Cache-Control: must-revalidate');
    header('Cache-Control: max-age=0');
    header('Pragma: public');

    ob_clean();
    flush();

    if (copy($filePath, 'php://output') === false) {
        return jsonReturn(-1, '');
    }
    // Delete temporary file
    @unlink($filePath);
    die();
}

/**
 * 参数过滤
 * @param $class
 * @return array
 * @throws array
 */
function requestFilter($class)
{

    try {

        $class = new \ReflectionClass($class);

        $properties = $class->getProperties();
        $propertiesMap = [];
        foreach ($properties as $vo) {
            $propertiesMap[] = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $vo->name));
        }

        return \think\facade\Request::only($propertiesMap);
    } catch (\ReflectionException $e) {

        return [];
    }
}

/**
 * 获取设备信息
 * @param $ua
 * @return array
 */
function getDeviceInfo($ua)
{
    $deviceOs = '未知设备';
    $deviceVersion = '未知版本';
    // $ua = $_SERVER['HTTP_USER_AGENT'];
    if (strpos($ua, 'Android') !== false) {

        preg_match("/(?<=Android )[\d\.]{1,}/", $ua, $version);
        $deviceOs = 'Android';
        $deviceVersion = $version[0];
    } elseif (strpos($ua, 'iPhone') !== false) {

        preg_match("/(?<=CPU iPhone OS )[\d\_]{1,}/", $ua, $version);
        $deviceOs = 'iPhone';
        $deviceVersion = str_replace('_', '.', $version[0]);
    } elseif (strpos($ua, 'iPad') !== false) {

        preg_match("/(?<=CPU OS )[\d\_]{1,}/", $ua, $version);
        $deviceOs = 'iPad';
        $deviceVersion = str_replace('_', '.', $version[0]);

    } elseif (preg_match('/OmniWeb\/(v*)([^\s|;]+)/i', $ua, $regs)) {

        $deviceOs = 'OmniWeb';
        $deviceVersion = $regs[2];
    } elseif (preg_match('/Netscape([\d]*)\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'Netscape';
        $deviceVersion = $regs[2];
    } elseif (preg_match('/safari\/([^\s]+)/i', $ua, $regs) && !preg_match('/Chrome\/([^\s]+)/i', $ua, $regs2)) {

        $deviceOs = 'Safari';
        $deviceVersion = $regs[1];
    } elseif (preg_match('/MSIE\s([^\s|;]+)/i', $ua, $regs)) {

        $deviceOs = 'Internet Explorer';
        $deviceVersion = $regs[1];
    } elseif (preg_match('/Opera[\s|\/]([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'Opera';
        $deviceVersion = $regs[1];
    } elseif (preg_match('/NetCaptor\s([^\s|;]+)/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) NetCaptor';
        $deviceVersion = $regs[1];
    } elseif (preg_match('/Maxthon/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) Maxthon';
        $deviceVersion = '';
    } elseif (preg_match('/360SE/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) 360SE';
        $deviceVersion = '';
    } elseif (preg_match('/SE 2.x/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) 搜狗';
        $deviceVersion = '';
    } elseif (preg_match('/FireFox\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'FireFox';
        $deviceVersion = $regs[1];
    } elseif (preg_match('/Lynx\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'Lynx';
        $deviceVersion = $regs[1];
    } elseif (preg_match('/Chrome\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'Chrome';
        $deviceVersion = $regs[1];
    } elseif (strpos($ua, 'Postman') !== false) {
        $deviceOs = 'Postman';
        $deviceVersion = '';
    }

    return [
        'deviceOs' => $deviceOs,
        'deviceVersion' => $deviceVersion
    ];
}


/**
 * 根据ip定位
 * @param $ip
 * @param $type
 * @return string | array
 * @throws Exception
 */
function getLocationByIp($ip, $type = 1)
{
    $ip2region = new \Ip2Region();
    $info = $ip2region->btreeSearch($ip);
    if (empty($info) || empty($info['region'])) {
        return ['province' => '未知', 'city' => '未知'];
    }
    $info = explode('|', $info['region']);

    $address = '';
    foreach ($info as $vo) {
        if ('0' !== $vo) {
            $address .= $vo . '-';
        }
    }

    if (2 == $type) {
        if (empty(array_filter($info))) {
            return ['province' => '未知', 'city' => '未知'];
        }
        return ['province' => $info['2'], 'city' => $info['3']];
    }

    return rtrim($address, '-');
}

/**
 * 快速排序
 * @param $arr
 */
function quickSort(&$arr)
{
    $length = count($arr);

    if ($length <= 1) {
        return;
    }

    $middle = $arr[0];

    $left = [];
    $right = [];

    for ($i = 1; $i < $length; $i++) {
        if ($middle['pivot']['sort'] < $arr[$i]['pivot']['sort']) {
            $right[] = $arr[$i];
        } else {
            $left[] = $arr[$i];
        }
    }

    quickSort($left);
    quickSort($right);

    $arr = array_merge($left, [$middle], $right);

//    return $arr;
}

function makeFormTable($table)
{
    return config('database.connections.mysql.prefix') . 'diyform_' . $table;
}

/**
 * 获取当前请求所对应的语言
 * @return string
 */
function setLang(): string
{
    $baseUrl = trim(\request()->baseUrl(), '/');
    $firstPath = explode('/', $baseUrl)[0];
    $langArr = config('system.lang');
    if (empty($firstPath) || !in_array($firstPath, $langArr)) {
        $firstPath = config('lang.default_lang');
    }
    return $firstPath;
}

/**
 * 字符串命名风格转换
 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
 * @param string $name 字符串
 * @param integer $type 转换类型
 * @param bool $ucfirst 首字母是否大写（驼峰规则）
 * @return string
 */
function parse_name($name, $type = 0, $ucfirst = true)
{
    if ($type) {
        $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
            return strtoupper($match[1]);
        }, $name);
        return $ucfirst ? ucfirst($name) : lcfirst($name);
    }

    return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}

/**
 * 获取插件类名
 * @param string $name 插件名
 * @return string
 */
function get_plugin_class($name)
{
    $name = ucwords($name);
    $pluginDir = parse_name($name);
    $class = "plugins\\{$pluginDir}\\{$name}Plugin";
    return $class;
}

function url_root($url = "")
{
    $dual_host = array('aaa.pro', 'ac.cn', 'ac.kr', 'ac.mu', 'aca.pro', 'acct.pro', 'ae.org', 'ah.cn', 'ar.com', 'avocat.pro', 'bar.pro', 'biz.ki', 'biz.pl', 'bj.cn', 'br.com', 'busan.kr', 'chungbuk.kr', 'chungnam.kr', 'club.tw', 'cn.com', 'co.ag', 'co.am', 'co.at', 'co.bz', 'co.cm', 'co.com', 'co.gg', 'co.gl', 'co.gy', 'co.il', 'co.im', 'co.in', 'co.je', 'co.kr', 'co.lc', 'co.mg', 'co.ms', 'co.mu', 'co.nl', 'co.nz', 'co.uk', 'co.ve', 'co.za', 'com.af', 'com.ag', 'com.am', 'com.ar', 'com.au', 'com.br', 'com.bz', 'com.cm', 'com.cn', 'com.co', 'com.de', 'com.ec', 'com.es', 'com.gl', 'com.gr', 'com.gy', 'com.hn', 'com.ht', 'com.im', 'com.ki', 'com.lc', 'com.lv', 'com.mg', 'com.ms', 'com.mu', 'com.mx', 'com.nf', 'com.pe', 'com.ph', 'com.pk', 'com.pl', 'com.ps', 'com.pt', 'com.ro', 'com.ru', 'com.sb', 'com.sc', 'com.se', 'com.sg', 'com.so', 'com.tw', 'com.vc', 'com.ve', 'cpa.pro', 'cq.cn', 'daegu.kr', 'daejeon.kr', 'de.com', 'ebiz.tw', 'edu.cn', 'edu.gl', 'eng.pro', 'es.kr', 'eu.com', 'fin.ec', 'firm.in', 'fj.cn', 'game.tw', 'gangwon.kr', 'gb.com', 'gb.net', 'gd.cn', 'gen.in', 'go.kr', 'gov.cn', 'gr.com', 'gs.cn', 'gwangju.kr', 'gx.cn', 'gyeongbuk.kr', 'gyeonggi.kr', 'gyeongnam.kr', 'gz.cn', 'ha.cn', 'hb.cn', 'he.cn', 'hi.cn', 'hk.cn', 'hl.cn', 'hn.cn', 'hs.kr', 'hu.com', 'hu.net', 'idv.tw', 'in.net', 'incheon.kr', 'ind.in', 'info.ec', 'info.ht', 'info.ki', 'info.nf', 'info.pl', 'info.ve', 'jeju.kr', 'jeonbuk.kr', 'jeonnam.kr', 'jl.cn', 'jp.net', 'jpn.com', 'js.cn', 'jur.pro', 'jx.cn', 'kg.kr', 'kiwi.nz', 'kr.com', 'law.pro', 'ln.cn', 'me.uk', 'med.ec', 'med.pro', 'mex.com', 'mo.cn', 'ms.kr', 'ne.kr', 'net.af', 'net.ag', 'net.am', 'net.br', 'net.bz', 'net.cm', 'net.cn', 'net.co', 'net.ec', 'net.gg', 'net.gl', 'net.gr', 'net.gy', 'net.hn', 'net.ht', 'net.im', 'net.in', 'net.je', 'net.ki', 'net.lc', 'net.lv', 'net.mg', 'net.mu', 'net.my', 'net.nf', 'net.nz', 'net.ph', 'net.pk', 'net.pl', 'net.ps', 'net.ru', 'net.sb', 'net.sc', 'net.so', 'net.vc', 'net.ve', 'nm.cn', 'no.com', 'nom.ag', 'nom.co', 'nom.es', 'nom.ro', 'nx.cn', 'or.at', 'or.jp', 'or.kr', 'or.mu', 'org.af', 'org.ag', 'org.am', 'org.bz', 'org.cn', 'org.es', 'org.gg', 'org.gl', 'org.gr', 'org.hn', 'org.ht', 'org.il', 'org.im', 'org.in', 'org.je', 'org.ki', 'org.lc', 'org.lv', 'org.mg', 'org.ms', 'org.mu', 'org.my', 'org.nz', 'org.pk', 'org.pl', 'org.ps', 'org.ro', 'org.ru', 'org.sb', 'org.sc', 'org.so', 'org.uk', 'org.vc', 'org.ve', 'pe.kr', 'pro.ec', 'qc.com', 'qh.cn', 'radio.am', 'radio.fm', 're.kr', 'recht.pro', 'ru.com', 'sa.com', 'sc.cn', 'sc.kr', 'sd.cn', 'se.com', 'senet', 'seoul.kr', 'sh.cn', 'sn.cn', 'sx.cn', 'tj.cn', 'tw.cn', 'uk.com', 'uk.net', 'ulsan.kr', 'us.com', 'us.org', 'uy.com', 'web.ve', 'xj.cn', 'xz.cn', 'yn.cn', 'za.com', 'zj.cn');
    $url_arr = explode(".", $url);
    if (count($url_arr) <= 2) {
        $host = $url;
    } else {
        $last = array_pop($url_arr);
        $last_1 = array_pop($url_arr);
        $last_2 = array_pop($url_arr);
        $host = $last_1 . '.' . $last;
        if (in_array($host, $dual_host)) {
            $host = $last_2 . '.' . $last_1 . '.' . $last;
        }
    }
    return $host;
}

function random_code_type($length = 8, $type = 'alpha-number')
{
    $code_arr = array(
        'alpha' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'number' => '0123456789', 'sign' => '#$%@*-_',);
    $type_arr = explode('-', $type);
    foreach ($type_arr as $t) {
        if (!array_key_exists($t, $code_arr)) {
            trigger_error("Can not generate type ($t) code");
        }
    }
    $chars = '';
    foreach ($type_arr as $t) {
        $chars .= $code_arr[$t];
    }
    $chars = str_shuffle($chars);
    $number = $length > strlen($chars) - 1 ? strlen($chars) - 1 : $length;
    return substr($chars, 0, $number);
}

function get_sub_data($pid, &$subData, &$data)
{
    $param = [];
    if (is_array($pid)) {
        $param['in'] = $pid; //上级pid IN 查询
    } else {
        $param['eq'] = $pid; //上级 pid = 查询
    }

    //根据父节点，查找下级的子节点
    $subList = search_all($param, $data);
    if (empty($subList)) {
        return $subData;
    }

    $tempSubIds = array_column($subList, 'id');  //获取这个节点的所有下级id
    $subData = array_merge($subData, $subList); //用户下级数据合并

    get_sub_data($tempSubIds, $subData, $data);
}

function get_sub_ids($pid, &$subIds, &$data)
{
    $param = [];
    if (is_array($pid)) {
        $param['in'] = $pid; //上级pid IN 查询
    } else {
        $param['eq'] = $pid; //上级 pid = 查询
    }

    //根据父节点，查找下级的子节点
    $subList = search_all($param, $data);
    if (empty($subList)) {
        return $subIds;
    }

    $tempSubIds = array_column($subList, 'id');  //获取这个节点的所有下级id
    $subIds = array_merge($subIds, $tempSubIds); //用户下级ID合并

    get_sub_ids($tempSubIds, $subIds, $data);
}

//模拟数据表查询所有操作
function search_all($param, &$data)
{
    $result = [];

    if (array_key_exists('eq', $param)) { //等于操作
        $searchValue = $param['eq'];
        foreach ($data as $value) {
            if ($searchValue == $value['parent_id']) {
                $result[] = $value;
            }
        }
    }
    if (array_key_exists('in', $param)) { //in查询
        $searchArray = $param['in'];
        foreach ($data as $value) {
            if (in_array($value['parent_id'], $searchArray)) {
                $result[] = $value;
            }
        }
    }

    return $result;
}

function createFile($file)
{
    //循环遍历文件夹,循环条件文件夹不存在
    if (is_dir($file)) {
        return;
    }

    //强制将'\'转换成 '/'
    $file = str_replace('\\', '/', $file);
    $file = substr($file, 0, strrpos($file, '/'));

    //创建文件夹，若创建失败错误提示被抑制
    @mkdir($file);
    //获取上级文件夹路径地址
    $file = substr($file, 0, strrpos($file, '/'));
    // echo 'sss';
    //打印路径
    //echo $file . '';
    //调用自身方法，将上级目录路径传入
    createFile($file);

}

function mkdirs($dir, $mode = 0777) {
    if (! is_dir ( $dir )) {
        if (! mkdirs ( dirname ( $dir ) )) {
            return false;
        }
        if (! mkdir ( $dir, $mode )) {
            return false;
        }
    }
    return true;
}



if(!function_exists("createUserToken")){
    /**
     * @throws TokenException
     */
    function createUserToken($data, $jtiType='admin')
    {
        try{
            $jwtConfig = config("jwt")['stores']['token'];
            $jti = $jtiType.'_'.mt_rand(1000,9999).$data['uid'];
            $jwtObject = Jwt::getInstance()->setSecretKey($jwtConfig['signer_key'])->publish();
            $jwtObject->setAlg('HMACSHA256');
            $jwtObject->setExp(time()+$jwtConfig['refresh_ttL']); //过期时间
            $jwtObject->setIat(time()); // 发布时间
            $jwtObject->setIss('huocms'); // 发行人
            $jwtObject->setJti(md5($jti)); // jwt id 用于标识该jwt
            $jwtObject->setNbf(time()); // 在此之前不可用
            $jwtObject->setSub($jtiType); // 主题
            $jwtObject->setData($data);
            return $jwtObject->__toString();
        }catch (Exception $e){
            throw new TokenException("登录失败",-402);
        }
    }
}
